﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Primitives;
using System.Linq;
using System.Security.AccessControl;
using System.Text;
using System.Threading.Tasks;
using Lhn.Ds.Core.CarDevice;
using Lhn.Ds.Core.Controll;
using Lhn.Ds.Core.Exceptions;

namespace Lhn.Ds.Core.Service
{
    [Export()]
    public class UsbCardService
    {
        [Import] public UsbCardDevice UsbCardDevice { get; set; }
        public XKReader XkReader { get; set; } = DsContext.XkReader;
        private byte[] PWKey = new byte[6] {0x48, 0x82, 0x96, 0x45, 0x38, 0x28};

        private byte[] DefaultSerialKey => new byte[12]
            {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

        private byte[] CalKey(byte[] uid)
        {
            byte[] rt = new byte[6];
            for (var i = 0; i < 6; i++)
            {
                var k = i % 4;
                rt[i] = (byte) (uid[k] ^ PWKey[i]);
            }

            return rt;
        }

        public byte[] SelectCard()
        {
            return new byte[1];
            return XkReader.SelectCard();
        }

        public void Beep()
        {
            XkReader.Beep();
        }

        public byte[] ReadId()
        {
            return XkReader.ReadId();
        }

        public string ReadStrId()
        {
            return XkReader.ReadStrId();
        }

        public UInt64 HexStringToUint64(string hexStr)
        {
            var b = HexStringToBytes(hexStr);
            if (BitConverter.IsLittleEndian)
            {
                b = b.Reverse().ToArray();
            }

            return BitConverter.ToUInt32(b, 0);
        }

        public byte[] HexStringToBytes(string hexStr)
        {
            if (string.IsNullOrEmpty(hexStr))
            {
                return new byte[0];
            }

            if (hexStr.StartsWith("0x"))
            {
                hexStr = hexStr.Remove(0, 2);
            }

            var count = hexStr.Length;

            if (count % 2 == 1)
            {
                throw new ArgumentException("Invalid length of bytes:" + count);
            }

            var byteCount = count / 2;
            var result = new byte[byteCount];
            for (int ii = 0; ii < byteCount; ++ii)
            {
                var tempBytes = Byte.Parse(hexStr.Substring(2 * ii, 2), System.Globalization.NumberStyles.HexNumber);
                result[ii] = tempBytes;
            }

            return result;
        }

        private byte[] GetKey()
        {
            var id = ReadId();
            return CalKey(id);
        }

        /// <summary>
        /// 充值卡，清理ka的0扇区的1、2块，恢复密码为FFFFFF
        /// </summary>
        public void ResetCard(CardInfoModel cardInfo)
        {
            //比对要销卡的no
            var serialNo = ReadId();
            var serialNoStr = ReadStrId();
            if (cardInfo != null)
                if (!string.Equals(serialNoStr, cardInfo.Code, StringComparison.CurrentCultureIgnoreCase))
                {
                    throw new NoNotEqualException();
                }

            byte[] picckey = CalKey(serialNo);
            byte[] piccdata0_2 = new byte[49];
            XkReader.Write(1, 2, picckey, piccdata0_2, serialNo);
        }

        /// <summary>
        /// 针对新卡设置本系统密码
        /// </summary>
        public void SetSytechPassword(byte[] serial)
        {
            byte[] picckey = CalKey(serial);
            XkReader.SetPassword(DefaultSerialKey, picckey, serial, 0);
        }

        /// <summary>
        /// 针对本系统卡设置默认密码
        /// </summary>
        public void SetDefaultPassword(byte[] serial)
        {
            byte[] picckey = CalKey(serial);
            XkReader.SetPassword(picckey, DefaultSerialKey, serial, 0);
        }

        public void WriteCard(CardInfoModel cardInfo)
        {
            var serialNo = ReadId();
            var serialNoStr = ReadStrId();
            if (!string.Equals(serialNoStr, cardInfo.Code, StringComparison.CurrentCultureIgnoreCase))
                throw new NoNotEqualException();
            //序列化卡信息到卡上
            var userInfo = new UserCardInfo();
            var userRecord = new UserCardRecord();
            DeserialCarInfoModel(cardInfo, userInfo, userRecord);
            byte ctrlword = UsbCardDevice.GetCtrlWordByte(false, true, true, true);
            byte area = 0;
            byte keyA1B0 = 1;
            byte[] picckey = CalKey(serialNo);
            byte[] piccdata0_2 =
                TransLittenEndian(userInfo.GetBytesForCard())
                    .Concat(TransLittenEndian(userRecord.GetBytesForCard()))
                    .ToArray();
            XkReader.Write(1, 2, picckey, piccdata0_2, serialNo);
        }

        public bool IsSytechCard()
        {
            var serialNo = ReadId();
            var pwd = CalKey(serialNo);
            try
            {
                XkReader.Read(1, 1, pwd, serialNo);
                return true;
            }
            catch (PasswordException)
            {
                return false;
            }
        }

        public CardInfoModel ReadCard()
        {
            //读取ka信息
            var serialNo = ReadId();
            var serialNoStr = ReadStrId();

            byte[] picckey = CalKey(serialNo);

            var data = XkReader.Read(1, 2, picckey, serialNo);
            data = TransLittenEndian(data);
            var userCarInfo = new UserCardInfo(data.Take(16).ToArray());
            var userCarRecord = new UserCardRecord(data.Skip(16).Take(16).ToArray());
            CompareCRC(userCarInfo);
            CompareCRC(userCarRecord);
            var carModel = new CardInfoModel() {Code = serialNoStr};
            SerialCardInfoModel(userCarInfo, userCarRecord, carModel);
            return carModel;
        }

        /// <summary>
        /// 序列化成CardInfoModel
        /// </summary>
        /// <param name="cardInfo"></param>
        /// <param name="cardRecord"></param>
        /// <param name="model"></param>
        private void SerialCardInfoModel(UserCardInfo cardInfo, UserCardRecord cardRecord, CardInfoModel model)
        {
            var cardNo = new CardNo() {Sn = Encoding.ASCII.GetString(cardInfo.Sn.TakeWhile(b => b>0).ToArray()),Number = cardInfo.Sn.Length==0? BitConverter.ToUInt32(cardInfo.CardNo, 0) : BitConverter.ToUInt64(cardInfo.CardNo, 0) };
            model.Count = BitConverter.ToUInt16(cardRecord.Count, 0);
            model.Flags = cardRecord.Flags[0];
            model.Total = BitConverter.ToUInt32(cardRecord.Total, 0);
            model.Balance = BitConverter.ToInt32(cardRecord.Remain, 0);
            model.CardState = (CardState) cardInfo.Status[0];
            model.Type = (CarTypeEnum) cardInfo.Type[0];
            model.Numer = cardNo.ToString();
        }


        /// <summary>
        /// 分别对2个块进行翻转
        /// </summary>`
        /// <param name="data"></param>
        /// <returns></returns>
        private byte[] TransLittenEndian(byte[] data)
        {
            if (BitConverter.IsLittleEndian) return data;
            byte[] nd = new byte[0];
            return data.Take(16).Reverse().Concat(data.Skip(16).Take(16).Reverse())
                .Concat(data.Skip(32).Take(16).Reverse()).ToArray();
        }

        private void CompareCRC(ICRCContent content)
        {
            var crc = UsbCardDevice.GetCRC8(content.GetContentBytes());
            if (crc != content.GetCRCContentBytes().First()) throw new CardCRCNotEqualxception();
        }


        private void CheckError(byte state)
        {
            switch (state)
            {
                case 0: return;
                case 8: throw new CardNotFoundException();
                case 12: throw new CardPasswordException();
                default: throw new CardException(UsbCardDevice.GetDefineCardMessage(state));
            }
        }

        /// <summary>
        /// 反序列话CardInfoModel 到UserCardInfo、UserCardRecord
        /// </summary>
        /// <param name="cardInfoModel"></param>
        /// <param name="cardInfo"></param>
        /// <param name="cardRecord"></param>
        private void DeserialCarInfoModel(CardInfoModel cardInfoModel, UserCardInfo cardInfo, UserCardRecord cardRecord)
        {
            var cardNo = ParseCarNoFromString(cardInfoModel.Numer);
            cardInfo.Status = new byte[] {(byte) cardInfoModel.CardState};
            cardInfo.CardNo = BitConverter.GetBytes(cardNo.Number);
            cardInfo.Type = new byte[] {(byte) (UInt32) cardInfoModel.Type};
            cardInfo.Sn = Encoding.ASCII.GetBytes(cardNo.Sn).Concat(new byte[5]).Take(5).ToArray();
            cardRecord.Count = BitConverter.GetBytes((UInt16) cardInfoModel.Count);
            cardRecord.Flags = new byte[] {(byte) (uint) cardInfoModel.Flags};
            cardRecord.Remain = BitConverter.GetBytes((Int32) cardInfoModel.Balance);
            cardRecord.Total = BitConverter.GetBytes((UInt32) cardInfoModel.Total);
            cardInfo.CRC = new byte[] {UsbCardDevice.GetCRC8(TransLittenEndian((cardInfo.GetContentBytes())))};
            cardRecord.CRC = new byte[] {(UsbCardDevice.GetCRC8(TransLittenEndian(cardRecord.GetContentBytes())))};
        }

        public CardNo ParseCarNoFromString(string str)
        {
            if (string.IsNullOrWhiteSpace(str) || str.Length != 13) throw new SytechException("长度应该是13");
            var s = str.Substring(0, 2);
            if (s.Count(c => (c >= 'A' && c <= 'Z')) != 2) throw new SytechException("SN公司序号错误，应全部大写");
            UInt64 n = 0;
            if (!UInt64.TryParse(str.Substring(2), out n)) throw new SytechException("数字序号部分错误");
            var no = new CardNo {Sn = s, Number = n};
            if (no.Year < 2018) throw new SytechException("年份应大于等于2018");
            if (!no.IsMonth) throw new SytechException("月份部分错误");
            if (no.No == 0) throw new SytechException("计数部分错误");
            return no;
        }
    }

    interface ICRCContent
    {
        byte[] GetContentBytes();
        byte[] GetCRCContentBytes();
    }

    public class CardNo
    {
        public string Sn { get; set; }
        public UInt64 Number { get; set; }
        public UInt64 Year => Number / 10000000;
        public UInt64 Month => Number % 10000000 / 100000;
        public UInt64 No => Number % 100000;
        public bool IsMonth => Month > 0 && Month < 13;

        public override string ToString()
        {
            return Sn + Number.ToString();
        }
    }


    public class UserCardInfo : ICRCContent
    {
        //5 ascii
        public byte[] Sn { get; set; }

        //8
        public byte[] CardNo { get; set; }

        //1
        public byte[] Type { get; set; }

        //1
        public byte[] Status { get; set; }

        //1
        public byte[] CRC { get; set; }

        public byte[] GetContentBytes()
        {
            return CardNo.Concat(Type).Concat(Status).Concat(Sn).ToArray();
        }

        public byte[] GetBytesForCard()
        {
            var data = GetContentBytes().Concat(GetCRCContentBytes()).ToArray();
            if (data.Length < 16)
            {
                data = data.Concat(new byte[16 - data.Length]).ToArray();
            }

            return data;
        }

        public byte[] GetCRCContentBytes()
        {
            return CRC;
        }

        public UserCardInfo()
        {
        }

        public UserCardInfo(byte[] data)
        {
            if (data.Reverse().TakeWhile(b => b == 0).Count() == 9)
            {
                //旧卡
                CardNo = data.Take(4).ToArray();
                Type = data.Skip(4).Take(1).ToArray();
                Status = data.Skip(5).Take(1).ToArray();
                Sn =new byte[0];
                CRC = data.Skip(6).Take(1).ToArray();
            }
            else
            {
                CardNo = data.Take(8).ToArray();
                Type = data.Skip(8).Take(1).ToArray();
                Status = data.Skip(9).Take(1).ToArray();
                Sn = data.Skip(10).Take(5).ToArray();
                CRC = data.Skip(15).Take(1).ToArray();
            }
        }
    }

    class UserCardRecord : ICRCContent
    {
        //4
        public byte[] Remain { get; set; }

        //4
        public byte[] Total { get; set; }

        //2
        public byte[] Count { get; set; }

        //1
        public byte[] Flags { get; set; }

        //1
        public byte[] CRC { get; set; }

        public UserCardRecord()
        {
        }

        public byte[] GetBytesForCard()
        {
            var data = GetContentBytes().Concat(GetCRCContentBytes()).ToArray();
            if (data.Length < 16)
            {
                data = data.Concat(new byte[16 - data.Length]).ToArray();
            }

            return data;
        }

        public UserCardRecord(byte[] data)
        {
            Remain = data.Take(4).ToArray();
            Total = data.Skip(4).Take(4).ToArray();
            Count = data.Skip(8).Take(2).ToArray();
            Flags = data.Skip(10).Take(1).ToArray();
            CRC = data.Skip(11).Take(1).ToArray();
        }


        public byte[] GetContentBytes()
        {
            return Remain.Concat(Total).Concat(Count).Concat(Flags).ToArray();
        }

        public byte[] GetCRCContentBytes()
        {
            return CRC;
        }
    }

    public class UnknowCardException : CardException
    {
        public UnknowCardException(string message = "未知卡类型") : base(message)
        {
        }
    }

    public class CardCRCNotEqualxception : CardException
    {
        public CardCRCNotEqualxception(string message = "CRC数据校验错误") : base(message)
        {
        }
    }

    public class CardNotFoundException : CardException
    {
        public CardNotFoundException(string message = "寻卡错误") : base(message)
        {
        }
    }

    public class CardPasswordException : CardException
    {
        public CardPasswordException(string message = "密码错误") : base(message)
        {
        }
    }


    public class CardException : BusinessException
    {
        public CardException(string message = "icCard操作错误") : base(message)
        {
        }
    }

    public class NoNotEqualException : BusinessException
    {
        public NoNotEqualException(string message = "卡编号不一致") : base(message)
        {
        }
    }
}